#!/bin/sh
#
#	netconf 0.2 - Configure network
#
# Changes:
#	v0.2: rewrite for NetBSD network infrastructure
#	 - the primary choice is now for an interface, not a network card;
#	 - manual driver configuration is now an exception;
#	 - the menu transition system is slightly more solid;
#	 - all non-interactive functionality has been removed.
#

# Get system config
. /etc/rc.conf

LOCALRC=/usr/etc/rc.local
IFCONF=/etc/ifconfig.
RESOLVCONF=/etc/resolv.conf
HOSTNAME=/etc/hostname.file
USRKBFILE=/.usrkb

prefix=""
cd="no" # running from cd?
changed="no" # have any ifconfig.if(5) files been changed?

usage()
{
    cat >&2 <<'EOF'
Usage:

  netconf [-lh] [-p <prefix>]

  flags:
     -l Print a list of configurable interfaces
     -h Print this help file
     -p Set a path prefix for all configuration files (e.g., /mnt)
EOF
    exit 1
}

backup_file()
{
	# Do not make backups if we're running from CD.
	if [ "$cd" != "yes" -a -f "$1" ]; then
		mv "$1" "$1~" || exit 1
		echo
		echo "Backed up $1 to $1~"
	fi
}

select_number()
{
	while true; do
		echo -n "$4 [$3] "
		read input
		case "$input" in
		'')
			return $3
			;;
		*[!0-9]*)
			;;
		*)
			[ $input -ge $1 -a $input -le $2 ] && return $input
			;;
		esac
	done
}

interfaces()
{
	# Get a list of interfaces that are not virtual (i.e., cloners). There
	# is always one virtual interface type ("lo", loopback).
	cloners_regex='^('`ifconfig -C | sed 's/ /[0-9]|/g'`'[0-9])'
	iflist=`ifconfig -l | tr ' ' '\n' | grep -vE "$cloners_regex"`

	ifcount=0
	ifunconf=0 # the first interface with no configuration file, or 0
	if [ -z "$iflist" ]; then
		echo "    No network hardware interfaces detected!"
	else
		for if in $iflist; do
			ifcount=$(($ifcount + 1))
			if [ -r $IFCONF$if ]; then
				info="($1)"
			else
				[ $ifunconf -eq 0 ] && ifunconf=$ifcount
				info=""
			fi
			printf "%2d. %-8s %s\n" $ifcount "$if" "$info"
		done
	fi
}

do_step1()
{
	echo "
The following network interfaces are available for configuration. These are
interfaces corresponding to network drivers that are currently running. If no
interface is listed for your network card here, then either MINIX 3 does not
support your card, or, if it is not a plug-and-play device, it may require
manual configuration first.

Please choose the interface you would like to configure, or another option.
"
	interfaces "already configured"
	echo
	manual_choice=$(($ifcount + 1))
	quit_choice=$(($ifcount + 2))
	printf "%2d. Manually configure an ethernet driver\n" $manual_choice
	printf "%2d. Quit\n\n" $quit_choice

	default_choice=$ifunconf
	[ $default_choice -eq 0 ] && default_choice=$quit_choice

	select_number 1 $quit_choice $default_choice "Interface choice?"
	choice=$?

	case $choice in
	$manual_choice)
		step=do_stepM
		;;
	$quit_choice)
		;;
	*)
		ifchoice="$(echo $iflist | cut -d' ' -f$choice)"
		step=do_step2
	esac
}

do_stepM()
{
	# TODO: it would be nice if this list changed on a per-platform basis..
	echo "
MINIX 3 has drivers for a limited number of older cards that require manual
configuration. They are shown below. Please choose one of the listed options.

 1. 3Com 501 or 3Com 509 based ISA card (i386)
 2. NE2000, 3Com 503, or WD based ISA card (i386) (emulated by Bochs, Qemu)

 3. Go back to interface selection
 4. Quit
"

	select_number 1 4 4 "Card choice?"

	case $? in
	1)
		driver=dpeth
		driverargs="#dpeth_args='DPETH0=port:irq:memory'"
		echo "
Note: After installing, edit $LOCALRC to the right configuration."
		;;
	2)
		driver=dp8390
		driverargs="dp8390_args='DPETH0=300:9'"
		echo "
Note: After installing, edit $LOCALRC to the right configuration.
You may then also have to edit /etc/system.conf.d/dp8390 to match.
For now, the defaults for emulation by Bochs/Qemu have been set."
		;;
	3)
		step=do_step1
		return
		;;
	4)
		return
		;;
	esac

	backup_file "$LOCALRC"
	echo "# Generated by netconf(8). Edit as necessary." > $LOCALRC
	echo "netdriver='"$driver"'" >> $LOCALRC
	echo "$driverargs" >> $LOCALRC

	# $LOCALRC typically expands to /mnt/usr/etc/rc.local, so leave room..
	echo "
A template to start the driver has been written to $LOCALRC . As
noted above, you may have to edit it. Once you are done editing, reboot the
system, after which the driver will be started. Once the driver is running,
you can run 'netconf' to configure the corresponding network interface."
}

do_step2()
{
	iffile="$IFCONF$ifchoice"

	echo "
Configure interface $ifchoice using DHCP or manually?

For now, the choice here is primarily about IPv4. With DHCP it is possible to
enable IPv6 as well. Even if the local network has no IPv6 facilities, enabling
IPv6 should do no harm. For IPv6-only mode or any other configuration that is
not supported here, you will have to edit $iffile yourself.

 1. Automatically using DHCP (IPv4 + IPv6)
 2. Automatically using DHCP (IPv4 only)
 3. Manually (IPv4 only)"

	if [ -r "$iffile" ]; then
		echo " 4. Remove current configuration"
		remove_choice=4
		goback_choice=5
		quit_choice=6
	else
		remove_choice=X
		goback_choice=4
		quit_choice=5
	fi

	echo
	printf "%2d. Go back to interface selection\n" $goback_choice
	printf "%2d. Quit\n\n" $quit_choice

	select_number 1 $quit_choice 1 "Configuration choice?"

	case $? in
	1)
		backup_file "$iffile"
		echo 'up' > $iffile
		echo '!dhcpcd -qM $int' >> $iffile

		echo
		echo "Interface $ifchoice configured for DHCP (IPv4 + IPv6)."
		step=do_step3
		;;
	2)
		backup_file "$iffile"
		echo 'up' > $iffile
		echo '!dhcpcd -qM -4 $int' >> $iffile

		echo
		echo "Interface $ifchoice configured for DHCP (IPv4 only)."
		step=do_step3
		;;
	3)
		# Query user for settings
		#
		# Some of these settings (hostname, nameservers) do not apply
		# to just the selected interface. Still, they are what one has
		# to specify for a complete manual configuration. In order to
		# make manual configuration of multiple interfaces less
		# frustrating in this regard, offer defaults that match what
		# may just have been set already.

		echo

		# Hostname
		if [ -r $HOSTNAME ]; then
			hostname_default=$(cat $HOSTNAME)
		else
			hostname_default="minix"
		fi
		echo -n "Hostname [$hostname_default]: "
		read hostname
		if [ -z "$hostname" ]; then
			hostname="$hostname_default"
		fi

		# IP address
		ip=""
		while [ -z "$ip" ]; do
			echo -n "IP address []: "
			read ip
		done

		# Netmask
		echo -n "Netmask (optional) []: "
		read netmask
		[ -n "$netmask" ] && netmask=" netmask $netmask"

		# Gateway (no gateway is fine for local networking)
		echo -n "Gateway (optional) []: "
		read gateway

		# DNS Servers
		dns1_default="$(grep '^nameserver' $RESOLVCONF 2>/dev/null | \
		    sed '1q' | awk '{print $2}')"
		dns2_default="$(grep '^nameserver' $RESOLVCONF 2>/dev/null | \
		    sed '2q;d' | awk '{print $2}')"

		echo -n "Primary DNS Server [$dns1_default]: "
		read dns1
		[ -z "$dns1" ] && dns1="$dns1_default"

		if [ -n "$dns1" ]; then
			echo -n "Secondary DNS Server (optional) [$dns2_default]: "
			read dns2
			[ -z "$dns2" ] && dns2="$dns2_default"
		else
			dns2=""
		fi

		backup_file "$HOSTNAME"
		echo "$hostname" > $HOSTNAME
		hostname "$hostname"

		backup_file "$iffile"
		echo 'up' > $iffile
		echo "inet $ip$netmask" >> $iffile
		if [ -n "$gateway" ]; then
			echo "!route -q add default $gateway" >> $iffile
		fi

		if [ -n "$dns1" ]; then
			backup_file "$RESOLVCONF"
			echo "nameserver $dns1" > $RESOLVCONF
			if [ -n "$dns2" ]; then
				echo "nameserver $dns2" >> $RESOLVCONF
			fi
		fi

		echo
		echo "Interface $ifchoice configured manually."
		step=do_step3
		;;
	$remove_choice)
		backup_file "$iffile"
		rm -f "$iffile"
		echo
		echo "Removed configuration for interface $ifchoice."
		step=do_step3
		;;
	$goback_choice)
		step=do_step1
		;;
	esac
}

do_step3()
{

	# We get here only if one of the ifconfig.if(5) files have changed.
	changed="yes"

	echo "
Do you want to configure additional interfaces?

You can also invoke the 'netconf' command as root at any later time.

 1. Go back to interface selection
 2. Quit
"

	# Note that "quit" is deliberately the default choice: most people will
	# want to configure at most one interface, and keep pressing Enter in
	# order to make it through the setup procedure as easily as possible.
	select_number 1 2 2 "Menu choice?"

	[ $? -eq 1 ] && step=do_step1
}

# Parse options
while getopts "p:hl" arg; do
    case "$arg" in
	p) prefix=$OPTARG; ;;
	h) usage ;;
	l) echo "The following network hardware interfaces are detected:"
	   echo
	   interfaces "configured"
	   exit 0
	   ;;
	\?) echo "Unknown option -$OPTARG"; usage ;;
	:) echo "Missing required argument for -$OPTARG"; usage ;;
	*)  usage ;;
    esac
done

if [ -n "$prefix" ] ; then
    if [ ! -d $prefix ]; then
    	echo -e "It seems the supplied prefix (\`$prefix') is invalid."
    	exit 1
    fi
    LOCALRC=$prefix$LOCALRC
    IFCONF=$prefix$IFCONF
    RESOLVCONF=$prefix$RESOLVCONF
    HOSTNAME=$prefix$HOSTNAME
fi

if [ `whoami` != root ] ; then
    echo "Please run netconf as root."
    exit 1
fi

if ! ifconfig -l >/dev/null 2>&1; then
    echo "Unable to obtain a list of interfaces. Is the LWIP service running?"
    exit 1
fi

# Are we running from CD?
if [ -f "$USRKBFILE" ] ; then
    cd="yes" # We are running from CD
fi

# The interactive program.
step=do_step1
while [ $step != exit ]; do
	proc=$step
	step=exit
	$proc
done

# Skip printing this last bit of information if it will not actually work.  The
# fact that it will not work on the CD (i.e. from the setup program) at least
# yet, is also the reason why we do not simply issue the command ourselves
# right now.  We might reconsider this later.
if [ "$changed" = "yes" -a -z "$prefix" ]; then
	echo
	echo "One or more of the interface configuration files have been changed."
	echo "You can use the command 'service network restart' to reload them now."
fi

# Aesthetics.
echo

exit 0
